#include "Acceptor.h"
#include "Logger.h"
#include "InetAddress.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

//静态函数可以方便不和其他函数重名，只在当前源文件生效
static int createNonblocking(){
    /*
        | ：按位或操作，这是为了设置多个属性
        参数解析：
            SOCK_STREAM：指定套接字的类型为流套接字，一般用于TCP
            SOCK_NONBLOCK：设置套接字为非阻塞模式。在默认情况下，套接字通常处于阻塞模式，
                当执行某些操作（如 accept、read、write 等）时，如果操作不能立即完成，
                线程会被阻塞，直到操作完成。而设置为非阻塞模式后，这些操作如果不能立即完成，
                会立即返回一个错误码或特定的值，表示操作正在进行中但尚未完成，
                而不会阻塞线程，允许程序在等待操作完成的同时继续执行其他任务。
            SOCK_CLOEXEC：可以确保在子进程执行 exec 函数替换自身执行环境时，自动关闭该套接字，从而避免这些问题。
            IPPROTO_TCP：表示套接字所使用的协议为 TCP。它指定了在创建的套接字上进行通信时所遵循的具体协议规则。
                当与 AF_INET 和 SOCK_STREAM 一起使用时，明确了创建的是一个基于 IPv4 的 TCP 流套接字，
    */
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if(sockfd < 0){
        LOG_FATAL("%s:%s:%d, listen socket create error \n", __FILE__, __FUNCTION__, __LINE__);
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
    : loop_(loop)
    , acceptSocket_(createNonblocking())
    , acceptChannel_(loop, acceptChannel_.fd())
    , listening_(false)
{
    //封装sockfd
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(true);
    acceptSocket_.bindAddress(listenAddr);
    //TcpServer::start() Acceptor.listen 有新用户的连接，要执行一个回调confd=>channel=>subLoop
    //baseLoop=>acceptChannel_（listenfd) =>
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor(){
    acceptChannel_.disableAll();
    acceptChannel_.remove();
}

void Acceptor::listen(){
    listening_ = true;
    acceptSocket_.listen();
    acceptChannel_.enableReading(); //将channel注册到poller里，才可以让poller帮我们监听
}

//发生时机：当listenfd有事件发生，也就是有新用户连接
void Acceptor::handleRead(){
    InetAddress perrAddr;
    int connfd = acceptSocket_.accept(&perrAddr);
    if(connfd >= 0){
        if(newConnectionCallback_){
            //这里就是执行读事件回调函数
            newConnectionCallback_(connfd, perrAddr);
        }
        else{
            ::close(connfd);
        }
    }
    else{
        LOG_ERROR("%s:%s:%d accept error \n", __FILE__, __FUNCTION__, __LINE__);
        if(errno == EMFILE){
            //资源使用完毕
            LOG_ERROR("%s:%s:%d sockfd reached limit error \n", __FILE__, __FUNCTION__, __LINE__);
        }
    }
}